//: concurrency/waxomatic2/WaxOMatic2.java
// Using Lock and Condition objects.
package concurrency.waxomatic2;

import java.util.concurrent.*;
import java.util.concurrent.locks.*;

import static net.mindview.util.Print.*;

class Car
{
    private Lock lock = new ReentrantLock();
    
    private Condition condition = lock.newCondition();
    
    private boolean waxOn = false;
    
    public void waxed()
    {
        lock.lock();
        try
        {
            waxOn = true; // Ready to buff
            condition.signalAll();
        }
        finally
        {
            lock.unlock();
        }
    }
    
    public void buffed()
    {
        lock.lock();
        try
        {
            waxOn = false; // Ready for another coat of wax
            condition.signalAll();
        }
        finally
        {
            lock.unlock();
        }
    }
    
    public void waitForWaxing()
        throws InterruptedException
    {
        lock.lock();
        try
        {
            while (waxOn == false)
                condition.await();
        }
        finally
        {
            lock.unlock();
        }
    }
    
    public void waitForBuffing()
        throws InterruptedException
    {
        lock.lock();
        try
        {
            while (waxOn == true)
                condition.await();
        }
        finally
        {
            lock.unlock();
        }
    }
}

class WaxOn implements Runnable
{
    private Car car;
    
    public WaxOn(Car c)
    {
        car = c;
    }
    
    public void run()
    {
        try
        {
            while (!Thread.interrupted())
            {
                printnb("Wax On! ");
                TimeUnit.MILLISECONDS.sleep(200);
                car.waxed();
                car.waitForBuffing();
            }
        }
        catch (InterruptedException e)
        {
            print("Exiting via interrupt");
        }
        print("Ending Wax On task");
    }
}

class WaxOff implements Runnable
{
    private Car car;
    
    public WaxOff(Car c)
    {
        car = c;
    }
    
    public void run()
    {
        try
        {
            while (!Thread.interrupted())
            {
                car.waitForWaxing();
                printnb("Wax Off! ");
                TimeUnit.MILLISECONDS.sleep(200);
                car.buffed();
            }
        }
        catch (InterruptedException e)
        {
            print("Exiting via interrupt");
        }
        print("Ending Wax Off task");
    }
}

public class WaxOMatic2
{
    public static void main(String[] args)
        throws Exception
    {
        Car car = new Car();
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new WaxOff(car));
        exec.execute(new WaxOn(car));
        TimeUnit.SECONDS.sleep(5);
        exec.shutdownNow();
    }
} /*
   * Output: (90% match) Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On!
   * Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax
   * On! Exiting via interrupt Ending Wax Off task Exiting via interrupt Ending Wax On task
   */// :~
